#ifndef DBMODULE_H_
#define DBMODULE_H_
#include "../../../../nemosomen/DataModel/src/nemosomenBase.h"
#include <QtSql>
#include "../../include/dto/camData.h"

const std::string MODULE_DB = "MODULE_DB";




class DbConfig : public ModuleConfig {
public:
	Prop<std::string> *host;
	Prop<std::string> *db;
	Prop<std::string> *user;
	Prop<std::string> *pwd;
	DbConfig(DataPool *_pool,std::string _name,std::string _host,std::string _db,std::string _user,std::string _pwd):ModuleConfig(_pool,_name,MODULE_DB) {
		host = new Prop<std::string>(TEXT,"host",this);
		db = new Prop<std::string>(TEXT,"db",this);
		user = new Prop<std::string>(TEXT,"user",this);
		pwd = new Prop<std::string>(TEXT,"pwd",this);
		host->set(_host);
		db->set(_db);
		user->set(_user);
		pwd->set(_pwd);
	}
};

class Db :  public Module, public IdProvider {


	
	
public:

	Db(DbConfig *_conf):Module(_conf){
		init=false;
	}
	Db():Module(){
		init=false;
	}

	inline DbConfig* conf() {
		return (DbConfig*)config;
	}

	long getId() {
		printf("getId");
		if (!init) {
			initDb();
		}
		if (model_id.exec()) {
			model_id.next();
			long v = model_id.value(0).toLongLong();
			printf("%d\n",(int)v);
			return v;
		}
		else {
			printf(model_id.lastError().text().toLatin1().data());
			return 0;
		}
		
	}
	
	std::list<std::string> loadSavePoints(std::string key) {
		if (!init) {
			initDb();
		}
		std::list<std::string> list;
		load_savepoints.bindValue(0,key.data());
		load_savepoints.exec();
		while (load_savepoints.next()) {
			list.push_back(load_savepoints.value(0).toString().toUtf8().data());
		}
		return list;
	}
	
	Model* loadSavePoint(std::string key, std::string name) {
		printf(name.data());
		printf("\n");
		if (!init) {
			initDb();
		}
		load_savepoint.bindValue(0,key.data());
		load_savepoint.bindValue(1,name.data());
		load_savepoint.exec();
		printError(load_savepoint);
		if (load_savepoint.next()) {
			long id = load_savepoint.value(0).toLongLong();
			return loadInstance(id);
		}
		return 0;
	}
	
	Model* loadInstance(long id) {
		printf("LOAD %d\n",(int)id);
		QSqlQuery lin = load_instance_name();
		lin.bindValue(0,(qlonglong)id);
		lin.exec();
		
		if ( lin.next()) {
			std::string className = lin.value(1).toString().toUtf8().data();
			std::string name = lin.value(0).toString().toUtf8().data();
			Model *m = ((DataPool*)conf()->getPool())->createModel(className,name);
			
			QSqlQuery li = load_instance();
			
			li.bindValue(0,(qlonglong)id);
			li.bindValue(1,(qlonglong)id);
			li.exec();
			printError(li);
			while (li.next()) {
				long cid = li.value(0).toLongLong();
				unsigned int pid = li.value(1).toUInt();
				unsigned char type =(unsigned char) li.value(2).toUInt();
				int isn = li.value(3).toInt();
				printf("%d %d %d %d \n",(int)cid,pid,(int)type,isn);
				if (isn==0) {
					switch (type) {
					case TEXT:
						loadString(m,cid,pid);
						break;
					}
				}
				else {
					switch (type) {
					case MODEL:
						loadNModel(m,cid,pid);
						break;
					}
				}
			}
			return m;
		}
		return 0;
	}
	
	void loadString(Model *m,long cid, unsigned int pid) {
		printf("LoadString\n");
		QSqlQuery query = stringValue();
		query.bindValue(0,(qlonglong)cid);
		query.exec();
		if (query.next()) {
			Prop<std::string> *p = (Prop<std::string>*)m->getProp(pid);
			p->set(query.value(0).toString().toUtf8().data());
		}
	}

	void loadNModel(Model *m,long cid, unsigned int pid) {
		printf("LoadModel\n");
		QSqlQuery query = linkValue();
		query.bindValue(0,(qlonglong)cid);
		query.exec();
		if (query.next()) {
			NProp<Model*> *p = (NProp<Model*>*)m->getProp(pid);
			long mId = query.value(0).toLongLong();
			p->append(loadInstance(mId));
		}
	}
	
	void saveModel(Model *m,std::string name,std::string key) {
		long time = TimeHelper::currentTimeMillis();
		saveModel(m,time);
		create_savepoint.bindValue(0,(qlonglong)m->getId());
		create_savepoint.bindValue(1,(qlonglong)time);
		create_savepoint.bindValue(2,name.data());
		create_savepoint.bindValue(3,key.data());
		create_savepoint.exec();
		printError(create_savepoint);
	}
	
	void saveModel(Model *m) {
		long time = TimeHelper::currentTimeMillis();
		saveModel(m,time);
	}
	
	void saveModel(Model *m,long time) {
		printf("save\n");
		if (!init) {
			initDb();
		}
		if (!modelExists(m->getId())) {
			printf("createModel\n");
			modelCreate(m);
		}
		std::list<AbstractProp*> *pList = m->getProps();
		std::list<AbstractProp*>::iterator it=pList->begin();
		for (;it != pList->end();it++) {
			AbstractProp *a = (AbstractProp*)*it;
			if (a->isN()){
				if (a->getType()==MODEL) {
					NProp<Model*> *n = (NProp<Model*>*)a;
					int i;
					for (i=0;i<n->size();i++) {
						Model *model = n->get(i);
						printf("\nModel: ");
						printf(model->getName().data());
						printf("\n");
						saveLink(time,m->getId(),n->getIndex(),model->getId(),1);
						saveModel(model,time);
					}
				}
				else if (a->getType()==INT) {
					NProp<int> *n = (NProp<int>*)a;
					int i;
					for (i=0;i<n->size();i++) {
						int v = n->get(i);
						printf("intval: %d\n",v);
						saveInt(time,m->getId(),n->getIndex(),v,1);
					}
				}
				else if (a->getType()==LONG) {
					NProp<long> *n = (NProp<long>*)a;
					int i;
					for (i=0;i<n->size();i++) {
						long v = n->get(i);
						printf("longval: %d\n",(int)v);
						saveLong(time,m->getId(),n->getIndex(),v,1);
					}
				}
				else if (a->getType()==FLOAT) {
					NProp<float> *n = (NProp<float>*)a;
					int i;
					for (i=0;i<n->size();i++) {
						float v = n->get(i);
						printf("floatval: %f\n",v);
						saveFloat(time,m->getId(),n->getIndex(),v,1);
					}
				}
				else if (a->getType()==TEXT) {
					NProp<std::string> *n = (NProp<std::string>*)a;
					int i;
					for (i=0;i<n->size();i++) {
						std::string v = n->get(i);
						printf(v.data());
						printf(" StringVal\n");
						saveString(time,m->getId(),n->getIndex(),v,1);
					}
				}
			}
			else {
				if (a->getType()==MODEL) {
					Prop<Model*> *n = (Prop<Model*>*)a;		
					if (!n->isValueSet()) continue;
					Model *model = n->get();
					printf("\nModel: ");
					printf(model->getName().data());
					printf("\n");
					saveLink(time,m->getId(),n->getIndex(),model->getId(),0);
					saveModel(model,time);
				}
				else if (a->getType()==INT) {
					Prop<int> *n = (Prop<int>*)a;
					if (!n->isValueSet()) continue;
					int v = n->get();
					printf("intval: %d\n",v);
					saveInt(time,m->getId(),n->getIndex(),v,0);
				}
				else if (a->getType()==LONG) {
					Prop<long> *n = (Prop<long>*)a;
					if (!n->isValueSet()) continue;
					long v = n->get();
					printf("longval: %d\n",(int)v);
					saveLong(time,m->getId(),n->getIndex(),v,0);
				}
				else if (a->getType()==FLOAT) {
					Prop<float> *n = (Prop<float>*)a;
					if (!n->isValueSet()) continue;
					float v = n->get();
					printf("floatval: %f\n",v);
					saveFloat(time,m->getId(),n->getIndex(),v,0);
				}
				else if (a->getType()==TEXT) {
					Prop<std::string> *n = (Prop<std::string>*)a;
					if (!n->isValueSet()) continue;
					std::string v = n->get();
					printf(v.data());
					printf(" StringVal\n");
					saveString(time,m->getId(),n->getIndex(),v,0);
				}
			}
		}
	}
	
	
	
	void saveInt(long timestamp, long instanceid,int propertyid,int value,int isn) {
		prepareSave(&save_int,timestamp,instanceid,propertyid,INT,isn);
		save_int.bindValue(3,value);
		save_int.exec();
		printError(save_int);
	}

	void saveString(long timestamp, long instanceid,int propertyid,std::string value,int isn) {
			prepareSave(&save_string,timestamp,instanceid,propertyid,TEXT,isn);
			save_string.bindValue(3,QString(value.data()));
			save_string.exec();
			printError(save_string);
		}
	
	void saveLink(long timestamp, long instanceid,int propertyid,long value,int isn) {
			prepareSave(&save_link,timestamp,instanceid,propertyid,MODEL,isn);
			save_link.bindValue(3,(qlonglong)value);
			save_link.exec();
		}
	
	void saveLong(long timestamp, long instanceid,int propertyid,long value,int isn) {
			prepareSave(&save_long,timestamp,instanceid,propertyid,LONG,isn);
			save_long.bindValue(3,(qlonglong)value);
			save_long.exec();
		}
	
	void saveFloat(long timestamp, long instanceid,int propertyid,float value,int isn) {
			prepareSave(&save_int,timestamp,instanceid,propertyid,FLOAT,isn);
			save_float.bindValue(3,value);
			save_float.exec();
		}
	
	void saveMoviePart(std::string name, std::string filename,int start, int stop) {
		
		save_MoviePart.bindValue(0,QString(name.data()));
		save_MoviePart.bindValue(1,QString(filename.data()));
		save_MoviePart.bindValue(2,start);
		save_MoviePart.bindValue(3,stop);
		save_MoviePart.exec();
		printError(save_MoviePart);
	}
	
	MovieData* loadMoviePart(int id) {
		
		load_MoviePart.bindValue(0,id);
		load_MoviePart.exec();
		printError(load_MoviePart);
		if (load_MoviePart.next()) {
			MovieData *m = new MovieData((AbstractPool*)config->getPool(),"mov");
			m->filename->set(load_MoviePart.value(0).toString().toUtf8().data());
			m->start->set(load_MoviePart.value(1).toInt());
			m->stop->set(load_MoviePart.value(2).toInt());
			return m;
		}
		return 0;
	}
	
	MovieList* listMovieParts(std::string name) {
		std::ostringstream os;
		os << "%"<< name << "%";
		std::string _name = os.str();
		list_MoviePart.bindValue(0,QString(_name.data()));
		list_MoviePart.exec();
		printError(list_MoviePart);
		MovieList *list = new MovieList((AbstractPool*)config->getPool(),"mov");
		while (list_MoviePart.next()) {
			MovieData *m = new MovieData((AbstractPool*)config->getPool(),"mov");
			m->id->set(list_MoviePart.value(0).toInt());
			m->name->set(list_MoviePart.value(1).toString().toUtf8().data());
			m->filename->set(list_MoviePart.value(2).toString().toUtf8().data());
			list->movies->append(m);
		
		}
		return list;
	}
	
	void saveText(std::string text) {
			
		save_text.bindValue(0,QString(text.data()));
		save_text.exec();
			printError(save_text);
		}
	
	TextList* listText(std::string name) {
			std::ostringstream os;
			os << "%"<< name << "%";
			std::string _name = os.str();
			list_text.bindValue(0,QString(_name.data()));
			list_text.exec();
			printError(list_text);
			TextList *list = new TextList((AbstractPool*)config->getPool(),"text");
			while (list_text.next()) {
				TextData *m = new TextData((AbstractPool*)config->getPool(),"text");				
				m->text->set(list_text.value(0).toString().toLatin1().data());
				list->texts->append(m);
			}
			return list;
		}
	
private:
	bool init;
	QSqlDatabase db;
	QSqlQuery model_id;
	QSqlQuery model_exists;
	QSqlQuery model_create;
	QSqlQuery change_id;
	QSqlQuery change_create;
	QSqlQuery save_string;
	QSqlQuery save_link;
	QSqlQuery save_int;
	QSqlQuery save_float;
	QSqlQuery save_long;
	QSqlQuery create_savepoint;
	QSqlQuery load_savepoints;
	QSqlQuery load_savepoint;
	
	QSqlQuery load_MoviePart;
	QSqlQuery save_MoviePart;
	QSqlQuery list_MoviePart;
	
	QSqlQuery save_text;
	QSqlQuery list_text;
	
	QSqlQuery load_instance() {
		QSqlQuery _load_instance = QSqlQuery(db);
		_load_instance.prepare("SELECT c.ID, c.PROPERTYID, c.TYP,c.isn	FROM CHANGES c,	(SELECT PROPERTYID AS  pi,max(timest) AS mt FROM CHANGES WHERE INSTANCEID = ? GROUP BY PROPERTYID) mxt WHERE c.PROPERTYID=mxt.pi AND c.TIMEST=mxt.mt AND c.INSTANCEID=? ORDER BY c.TYP,c.isn");
		return _load_instance;
	}
	QSqlQuery load_instance_name() {
		QSqlQuery _load_instance_name = QSqlQuery(db);
		_load_instance_name.prepare("SELECT name,classname FROM instances WHERE id=?");
		return _load_instance_name;
	}
	
	

				
	QSqlQuery intValue() {
		QSqlQuery query = QSqlQuery(db);
		query.prepare("SELECT value FROM intvalues WHERE id=?");
		return query;
	}
	QSqlQuery floatValue() {
		QSqlQuery query = QSqlQuery(db);
		query.prepare("SELECT value FROM floatvalues WHERE id=?");
		return query;
	}
	QSqlQuery linkValue() {
		QSqlQuery query = QSqlQuery(db);
		query.prepare("SELECT value FROM linkvalues WHERE id=?");
		return query;
	}
	QSqlQuery stringValue() {
		QSqlQuery query = QSqlQuery(db);
		query.prepare("SELECT value FROM stringvalues WHERE id=?");
		return query;
	}
	QSqlQuery longValue() {
		QSqlQuery query = QSqlQuery(db);
		query.prepare("SELECT value FROM longvalues WHERE id=?");
		return query;
	}
	

	
	
	long getChangeId() {
			if (!init) {
				initDb();
			}
			if (change_id.exec()) {
				change_id.next();
				long v = change_id.value(0).toLongLong();
				return v;
			}
			else {
				printError(change_id);
				return 0;
			}
			
		}
	
	void prepareSave(QSqlQuery *query,long timestamp, long instanceid,int propertyid,unsigned char type,int isn) {
		long id = saveChange(timestamp,instanceid,propertyid,type, isn);
		query->bindValue(0,(qlonglong)id);
		query->bindValue(1,(qlonglong)instanceid);
		query->bindValue(2,propertyid);
	}
	
	long saveChange(long timestamp, long instanceid,int propertyid,unsigned char type,int isn) {
		long id = getChangeId();
		change_create.bindValue(0,(qlonglong)id);
		change_create.bindValue(1,(qlonglong)timestamp);
		change_create.bindValue(2,(qlonglong)instanceid);
		change_create.bindValue(3,propertyid);
		change_create.bindValue(4,type);
		change_create.bindValue(5,isn);
		change_create.exec();
		printError(change_create);
		return id;
	}
	
	void modelCreate(Model *m) {
		model_create.bindValue(0,(qlonglong)m->getId());
		model_create.bindValue(1,m->getName().data());
		model_create.bindValue(2,m->getClassName().data());
		model_create.exec();
		printError(model_create);
	}
	
	void printError(QSqlQuery query) {
		printf(query.lastError().text().toLatin1().data());
	}
	
	bool modelExists(long id) {
		model_exists.bindValue(0,(qlonglong)id);
		model_exists.exec();
		return model_exists.next();
	}
	
	void initDb() {

		printf("connecting to db\n");
		db = QSqlDatabase::addDatabase("QPSQL");
		db.setHostName(QString(conf()->host->get().data()));
		db.setDatabaseName(QString(conf()->db->get().data()));
		db.setUserName(QString(conf()->user->get().data()));
		db.setPassword(QString(conf()->pwd->get().data()));
		bool ok = db.open();
		if (ok) {
			printf("ok\n");
			init=true;
			model_id = QSqlQuery(db);
			model_id.prepare("SELECT nextval('model_ids')");
			model_exists = QSqlQuery(db);
			model_exists.prepare("SELECT id FROM instances WHERE id = ?");
			model_create = QSqlQuery(db);
			model_create.prepare("INSERT INTO instances (id,name,classname) VALUES (?,?,?)");
			change_id = QSqlQuery(db);
			change_id.prepare("SELECT nextval('change_ids')");
			change_create = QSqlQuery(db);
			change_create.prepare("INSERT INTO changes (id,timest,instanceid,propertyid,typ,isn) VALUES (?,?,?,?,?,?)");
			save_string = QSqlQuery(db);
			save_string.prepare("INSERT INTO stringvalues (id,instanceid,propertyid,value) VALUES (?,?,?,?)");
			save_link = QSqlQuery(db);
			save_link.prepare("INSERT INTO linkvalues (id,instanceid,propertyid,value) VALUES (?,?,?,?)");
			save_int = QSqlQuery(db);
			save_int.prepare("INSERT INTO intvalues (id,instanceid,propertyid,value) VALUES (?,?,?,?)");
			save_float = QSqlQuery(db);
			save_float.prepare("INSERT INTO floatvalues (id,instanceid,propertyid,value) VALUES (?,?,?,?)");
			save_long = QSqlQuery(db);
			save_long.prepare("INSERT INTO longvalues (id,instanceid,propertyid,value) VALUES (?,?,?,?)");
			create_savepoint = QSqlQuery(db);
			create_savepoint.prepare("INSERT INTO savepoint (instanceid,timest,name,\"key\") VALUES (?,?,?,?)");
			load_savepoints = QSqlQuery(db);
			load_savepoints.prepare("SELECT name FROM savepoint WHERE \"key\"=?");
			
			load_savepoint = QSqlQuery(db);
			load_savepoint.prepare("SELECT instanceid,timest FROM savepoint WHERE \"key\"=? AND name=?");
			
			
			load_MoviePart = QSqlQuery(db);
			load_MoviePart.prepare("SELECT filename,start,stop FROM moviepart WHERE id=?");
			
			save_MoviePart = QSqlQuery(db);
			save_MoviePart.prepare("INSERT INTO moviepart (name,filename,start,stop) VALUES (?,?,?,?)");
			
			list_MoviePart = QSqlQuery(db);
			list_MoviePart.prepare("SELECT id,name,filename FROM moviepart WHERE name LIKE ?");
			
			save_text = QSqlQuery(db);
			save_text.prepare("INSERT INTO texts (value) VALUES (?)");
			
			list_text = QSqlQuery(db);
			list_text.prepare("SELECT value FROM texts WHERE value LIKE ?");
		}
	}
};

class DbImport {
public:
	static void importModule(DataPool *_pool) {
		_pool->registerCreator(MODULE_DB,&(create<Db>));
	}
};

#endif /*DBMODULE_H_*/
